package com.chenliang.provider.config;

import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.ReturnedMessage;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.connection.CorrelationData;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;

/**
 * @program: sendRabbitMqMsg
 * @description:
 * @author: 陈亮
 * @create: 2022-05-05 21:53
 */
@Slf4j
@Configuration
public class RabbitMqConfig {


    /**
     * 从配置文件获取rabbitmq基础信息
     */
    @Value("${spring.rabbitmq.host}")
    private String host;
    @Value("${spring.rabbitmq.port}")
    private Integer port;
    @Value("${spring.rabbitmq.username}")
    private String username;
    @Value("${spring.rabbitmq.password}")
    private String password;
    @Value("${spring.rabbitmq.virtual-host}")
    private String virtualHost;
    @Value("${spring.rabbitmq.publisher-returns}")
    private Boolean publisherReturns;
    @Value("${spring.rabbitmq.publisher-confirms}")
    private Boolean publisherConfirms;


    /**
     * 构建rabbitMq的连接工厂
     *
     * @return
     */
    @Bean
    public ConnectionFactory connectionFactory() {
        CachingConnectionFactory connectionFactory = new CachingConnectionFactory(host, port);
        connectionFactory.setUsername(username);
        connectionFactory.setPassword(password);
        connectionFactory.setVirtualHost(virtualHost);
        // 开启确认模式(开启之后才可以调用confirm)
        connectionFactory.setPublisherConfirms(publisherConfirms);
        connectionFactory.setPublisherReturns(publisherReturns);
        return connectionFactory;
    }


    /**
     * 构建rabbitTemplate实实例
     *
     * @return
     */
    @Bean
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
    public RabbitTemplate rabbitTemplate() {

        RabbitTemplate template = new RabbitTemplate(this.connectionFactory());

        // 设置json格式序列化转化器
        template.setMessageConverter(this.jsonMessageConverter());


        template.setMandatory(true);

        /**
         *设置生产者发送消息到交换机是否成功的回调机制: 配合connectionFactory.setPublisherConfirms(true)开启使用
         *
         *这个可以观察消息是否从生产者发送到了交换机
         */
        template.setConfirmCallback((correlationData, ack, cause) -> {
            String id = correlationData.getId();
            if (ack) {
                log.info("id为:" + id + "的消息,发送到exchange成功");
            } else {
                log.info("id为:" + id + "的消息发送到exchange失败,原因:{}", cause);
            }
        });

        /**
         *
         *设置交换机发送消息到队列是否成功的回退机制: 配合template.setMandatory(true)使用
         *
         * 这个机制可以观察消息是否从交换机分发到了队列. 
         * 如果不设置template.setMandatory(true), 交换机分到到队列出现问题,默认就把消息丢失了
         * 设置了true.交换机分发消息到队列出现问题就会把消息退回给生产者
         */

        template.setReturnCallback(new RabbitTemplate.ReturnCallback() {

            /**
             *
             * @param message 消息对象
             * @param replyCode 失败错误码
             * @param replyText 错误信息
             * @param exchange 交换机
             * @param routingKey 路由
             */
            @Override
            public void returnedMessage(Message message, int replyCode, String replyText, String exchange, String routingKey) {
                System.out.println("退回了:" + message);
            }
        });

        return template;
    }

    /**
     * 不让rabbitMq设置jdk自动的序列化,设置json格式序列化
     *
     * @return
     */
    @Bean
    public MessageConverter jsonMessageConverter() {
        return new Jackson2JsonMessageConverter();
    }
}
